<?php

namespace common\models\rbac;

use Yii;
use yii\db\Query;
use mdm\admin\components\Configs;
use common\helpers\Url;

/**
 * This is the model class for table "menu".
 *
 * @property integer $id Menu id(autoincrement)
 * @property string $name Menu name
 * @property integer $parent Menu parent
 * @property string $route Route for this menu
 * @property integer $order Menu order
 * @property string $data Extra information for this menu
 *
 * @property Menu $menuParent Menu parent
 * @property Menu[] $menus Menu children
 *
 * @author Misbahul D Munir <misbahuldmunir@gmail.com>
 * @since 1.0
 */
class Menu extends \common\db\ActiveRecord {

    const VISIBLED = 1;
    const HIDDEN = 0;

    public $parent_name;

    /**
     * @inheritdoc
     */
    public static function getDb() {
        if (Configs::instance()->db !== null) {
            return Configs::instance()->db;
        }
        return parent::getDb();
    }

    /**
     * @inheritdoc
     */
    public static function tableName() {
        return Configs::instance()->menuTable;
    }

    /**
     * @inheritdoc
     */
    public function rules() {
        return [
            ['name', 'required'],
            ['parent_name', 'in', 'range' => static::find()->select(['name'])->column(), 'message' => 'Menu "{value}" not found.'],
            [['icon', 'parent', 'route', 'data', 'order'], 'default'],
            [
                'parent',
                'filterParent',
                'when' => function() {
                    return !$this->isNewRecord;
                }
            ],
            [['order', 'visibled'], 'integer'],
            ['route', 'in', 'range' => static::getSavedRoutes(), 'message' => 'Route "{value}" not found.']
        ];
    }

    /**
     * Use to loop detected.
     */
    public function filterParent() {
        $parent = $this->parent;
        $db = static::getDb();
        $query = (new Query)->select(['parent'])
        ->from(static::tableName())
        ->where('[[id]]=:id');
        while ($parent) {
            if ($this->id == $parent) {
                $this->addError('parent_name', 'Loop detected.');
                return;
            }
            $parent = $query->params([':id' => $parent])->scalar($db);
        }
    }

    /**
     * @inheritdoc
     */
    public function attributeLabels() {
        return [
            'id' => Yii::t('rbac-admin', 'ID'),
            'name' => Yii::t('rbac-admin', 'Name'),
            'icon' => Yii::t('rbac-admin', 'Icon'),
            'parent' => Yii::t('rbac-admin', 'Parent'),
            'parent_name' => Yii::t('rbac-admin', 'Parent Name'),
            'route' => Yii::t('rbac-admin', 'Route'),
            'order' => Yii::t('rbac-admin', 'Order'),
            'data' => Yii::t('rbac-admin', 'Data'),
            'visibled' => Yii::t('rbac-admin', 'Visibled'),
        ];
    }

    /**
     * Get menu parent
     * @return \yii\db\ActiveQuery
     */
    public function getMenuParent() {
        return $this->hasOne(Menu::className(), ['id' => 'parent']);
    }

    /**
     * Get menu parent
     * @return \yii\db\ActiveQuery
     */
    public function getTopParentId() {
        $pid = $this->id;
        $parent = $this->menuParent;
        while ($parent) {
            $pid = $parent->id;
            $parent = $parent->menuParent;
        }
        if ($pid == $this->id) {
            //return $this->parent;
        }
        return $pid;
    }

    /**
     * Get menu children
     * @return \yii\db\ActiveQuery
     */
    public function getMenus() {
        return $this->hasMany(Menu::className(), ['parent' => 'id']);
    }

    private static $_routes;

    /**
     * Get saved routes.
     * @return array
     */
    public static function getSavedRoutes() {
        if (self::$_routes === null) {
            self::$_routes = [];
            foreach (Configs::authManager()->getPermissions() as $name => $value) {
                self::$_routes[] = $name;
            }
        }
        return self::$_routes;
    }

    public static function getMenuSource() {
        $tableName = static::tableName();
        return (new Query())->select(['m.id', 'm.name', 'm.icon', 'm.route', 'parent_name' => 'p.name'])
        ->from(['m' => $tableName])
        ->leftJoin(['p' => $tableName], '[[m.parent]]=[[p.id]]')
        ->all(static::getDb());
    }

    /**
     * 格式化树形结构菜单
     * @param array $tree 需要格式化的菜单数组
     * @param int|null $pid 父级菜单ID
     * @return array 返回格式化后的树形结构菜单
     */
    private static function treeFormat($tree, $pid = 0) {
        $return = [];
        foreach ($tree as $leaf) {
            if ($leaf['pid'] !== $pid) {
                continue;
            }
            $temp = [
                'label' => $leaf['name'],
                'url' => [$leaf['route']],
                'icon' => $leaf['icon'],
            ];
            foreach ($tree as $subleaf) {
                if ($subleaf['pid'] == $leaf['id']) {
                    $temp['items'] = self::treeFormat($tree, $leaf['id']);
                    $temp['url'] = 'javascript:;';
                    break;
                }
            }
            if (strpos($leaf['route'], '/*')) {
                continue;
            }
            if (isset($temp['items']) && count($temp['items']) == 1) {
                $temp = array_shift($temp['items']);
            }
            $return[] = $temp;
        }
        return $return;
    }

    /**
     * 获取当前模块授权菜单格式化后的菜单内容
     * @param string $uniqueId 模块的名称
     * @param array $authMenus 传入的授权路由内容
     * @return array 返回格式化后的菜单
     */
    public static function loadMenus($uniqueId, $authMenus) {
        $data = static::find()
        ->select(['id', 'name', 'route', 'icon', 'parent as pid'])
        ->where(['visibled' => self::VISIBLED])
        ->andWhere(['like', 'route', '/' . $uniqueId . '/%', false])
        ->asArray()
        ->all();
        if (empty($data)) {
            return $data;
        }
        $parentid = null;
        foreach ($data as $key => $menu) {
            if ($menu['pid'] == null) {
                $parentid = $menu['id'];
                continue;
            }
            if (strpos($menu['route'], '/*') === false && !in_array($menu['route'], $authMenus)) {
                unset($data[$key]);
            }
        }
        return self::treeFormat($data, $parentid);
    }

    public function getIconPath() {
        if ($this->icon) {
            if (strstr($this->icon, '/') !== false) {
                return Url::attach($this->icon);
            }
            return $this->icon;
        }
        if ($this->parent == null) {
            return 'icon-module icon-state-success';
        } else {
            if (empty($this->menus)) {
                return 'icon-action icon-state-info';
            }
            return 'icon-controller icon-state-info';
        }
        return '';
    }

    public function getChildNodes() {
        return !empty($this->menus);
    }

    public function getNodeType() {
        return $this->parent == null ? 'root' : '';
    }

    public function getNodeState() {
        if ($this->visibled == static::HIDDEN) {
            return ['disabled' => true];
        }
        return null;
    }

    public function fields() {
        return [
            'id',
            'text' => 'name',
            'type' => 'nodeType',
            'icon' => 'iconPath',
            'state' => 'nodeState',
            'children' => 'childNodes',
        ];
    }

}
